package sve;

import graph.Graph;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Stack;

import java.util.List;

import org.omg.CORBA.OBJ_ADAPTER;

import sve.Pair;
import xadd.XADD;
import xadd.XADD.DeltaFunctionSubstitution;
import cmdp.CMDP;
import cmdp.HierarchicalParser;

public class SVE {
	private Buffer _buffer;

	/**
	 * @param filename
	 *            is the path to the .dp and con files. Same name has to be used for them both.
	 */
	public void load(String filename) {
		_buffer = Buffer.parse(filename + ".sve");
		// for (String var : _buffer.getVarIds().keySet()) {
		// int id = _buffer.getVarIds().get(var);
		// Graph g = _buffer.getCases().get(var).getGraph(id);
		// Utils.print(_buffer.getCases().get(var).collectVars(id));
		// List l = g.topologicalSort(false);
		// g.launchViewer();
		// System.out.println(var + " <<<<<<<<");
		// for (Object o : l) {
		// System.out.println(o.toString());
		// }
		// }
		// xadd.XADD xadd = new XADD();
		// ArrayList a = HierarchicalParser.ParseFile(filename + ".sve");
		// // ArrayList c = HierarchicalParser.ParseFile(filename + ".b");
		// // extractBounds(c);
		// int id = xadd.buildCanonicalXADD((ArrayList) a.get(1));
		// Graph g = xadd.getGraph(id);
		// List l = g.topologicalSort(false);
		// for (Object o : l) {
		// System.out.println( o.toString() );
		// }
		//
		// g.launchViewer();
	}

	public Pair<Integer, XADD> solve(String query, String evidence) {
		Pair<Integer, XADD> solution = null;
		String[] qu = query.split(",");
		String[] ev = evidence.split(",");
		ArrayList<ArrayList<Integer>> q = _buffer.getIds(qu), e = _buffer.getIds(ev);

		for (int qidx = 0; qidx < qu.length; qidx++) {
			int qID = q.get(0).get(qidx);
			XADD qXADD = _buffer.getCase(qID);
			ArrayList<String> s = getSortedVars(qXADD, qID);
			// qXADD.getGraph(qID).launchViewer();
			for (int i = 0; i < s.size(); i++) {
				String var = s.get(i);
				if (Utils.contains(var, qu) || Utils.contains(var, ev))
					continue;

				ArrayList<ArrayList<Integer>> references = _buffer.getReferences(var);
				int t = -1;
				for (int j = 0; j < references.get(0).size(); j++) {
					if (_buffer.isBoolean(var)) {
						int restrict_high = qXADD.opOut(qID, qXADD.getBoolVarIndex(var), XADD.RESTRICT_HIGH);
						int restrict_low = qXADD.opOut(qID, qXADD.getBoolVarIndex(var), XADD.RESTRICT_LOW);
						qID = qXADD.apply(restrict_high, restrict_low, XADD.SUM);
					} else {
						t = (t > -1) ? qXADD.apply(t, references.get(0).get(j), XADD.PROD) : references.get(0).get(0);
					}
				}
				if (!_buffer.isBoolean(var)) {
					qID = qXADD.computeDefiniteIntegral(t, var);
				}

				for (int j = 0; j < references.get(1).size(); j++) {
					qID = qXADD.reduceProcessXADDLeaf(qID, qXADD.new DeltaFunctionSubstitution(var, references.get(1)
							.get(j)), true);
				}
			}
			solution = new Pair<Integer, XADD>(qID, qXADD);
		}

		solution.getValue().getGraph(solution.getKey()).launchViewer();

		return solution;
	}

	// private Stack<String> getSortedVars() {
	// HashMap<String, List> sorted = new HashMap<String, List>();
	// int count = 0;
	// Stack<String> s = new Stack<String>();
	//
	// for (String var : _buffer.getVars()) {
	// if (s.contains(var))
	// continue;
	// XADD x = _buffer.getCases().get(var);
	// Graph g = x.getGraph(_buffer.getId(var));
	// List l = g.topologicalSort(true); // the first ones are the leaves
	// for (Object o : l) {
	// int id = Integer.parseInt((String) o);
	// HashSet<String> cv = x.getNode(id).collectVars();
	// if (cv.size() == 0 || (cv.size() == 1 && cv.contains(var))) {
	// s.add(0, var);
	// break;
	// } else if (Utils.isInStack(cv, s)) {
	// s.push(var);
	// break;
	// }
	// }
	// sorted.put(var, l);
	// count = Math.max(count, l.size());
	// }
	// return s;
	// }

	private static void checkVarsInStack(String var, ArrayList<String> stack, HashSet<String> cv) {
		int idx = Integer.MAX_VALUE;
		for (String ss : cv) {
			if (!stack.contains(ss) && !ss.equals(var)) {
				stack.add(ss);
				idx = stack.indexOf(ss);
			}
		}
		if (stack.contains(var))
			stack.remove(var);
		stack.add(idx == Integer.MAX_VALUE ? 0 : idx, var);
	}

	private ArrayList<String> getSortedVars(XADD xadd, int xaddId) {
		ArrayList<String> s = new ArrayList<String>();

		for (String var : xadd.collectVars(xaddId)) {
			XADD x = _buffer.getCases().get(var);
			int id = _buffer.getId(var);
			HashSet<String> cv = x.collectVars(id);
			checkVarsInStack(var, s, cv);
		}
		return s;
	}

}
